Deploying Cocoa applications

(this page is obsolete, since deployment is now a built-in feature of recent CHICKEN versions)

Similar to the solution for Deploying Linux binaries, we can modify the link-paths of a Scheme application that uses the objc egg to create a fully self-contained Cocoa application bundle.

First, build the "Temperature Converter" application, but adding a small prologue that retrieves the physical location of the current executable and sets the "repository path", the path where extensions are to be loaded at run-time:

% cd tests/Temperature\ Converter.app/Contents/MacOS
% csc -X objc -O2 -o "TemperatureConverter" temp-converter.scm -v -framework CoreFoundation -prologue osx-deploy-bundle.scm

The prologue code looks like this:

;;;; osx-deploy-bundle.scm
;
; Use like this:
;
; % csc <your-application-main-module> -prologue osx-deploy-bundle.scm -framework CoreFoundation

(use utils)

#>
#include <CoreFoundation/CoreFoundation.h>
<#

#>!
static char *get_bundle_path()
{
  CFBundleRef bundle = CFBundleGetMainBundle();
  CFURLRef url = CFBundleCopyExecutableURL(bundle);
  static char buffer[ 256 ];
  
  if(CFURLGetFileSystemRepresentation(url, true, buffer, sizeof(buffer))) return buffer;
  else return NULL;
}
<#

(let ((application-path (get_bundle_path)))
  (assert application-path "unable to compute executable path")
  (repository-path (pathname-directory application-path) ) )

After building the application in the objc egg, we can look at the libraries that are used at run-time:

% otool -L TemperatureConverter
TemperatureConverter:
	/usr/lib/libchicken.0.dylib (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)
	/usr/lib/libffi.4.dylib (compatibility version 5.0.0, current version 5.1.0)

Then we use install_name_tool to change the load-paths of the two non-system libraries to point to the location where the executable (TemperatureConverter) is located:

% install_name_tool -change /usr/lib/libchicken.0.dylib @executable_path/libchicken.0.dylib \
  -change /usr/lib/libffi.4.dylib @executable_path/libffi.4.dylib  TemperatureConverter
% otool -L TemperatureConverter
TemperatureConverter:
	@executable_path/libchicken.0.dylib (compatibility version 1.0.0, current version 1.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.0.0)
	@executable_path/libffi.4.dylib (compatibility version 5.0.0, current version 5.1.0)

Now copy the required libraries and the compiled extensions into the directory, for Temperature Converter, you'll need the following:

Now you application is complete - the whole Temperature Converter.app bundle can be moved to a machine that doesn't have CHICKEN or the objc egg installed.

To verify this, run it with the Console application open:

% DYLD_PRINT_LIBRARIES=1 open Temperature\ Converter.app/

===== Friday, April 21, 2006 10:24:06 PM Europe/Berlin =====
dyld: loaded: /Users/felix/tmp/t2old/Temperature  Converter.app/Contents/MacOS/TemperatureConverter
dyld: loaded: /Users/felix/tmp/t2old/Temperature Converter.app/Contents/MacOS/libchicken.0.dylib
dyld: loaded:  /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
dyld: loaded: /usr/lib/libSystem.B.dylib, cpu-sub-type: 0
dyld: loaded: /Users/felix/tmp/t2old/Temperature  Converter.app/Contents/MacOS/libffi.4.dylib
...
dyld: loaded: /Users/felix/tmp/t2old/Temperature Converter.app/Contents/MacOS/objc-support.so
...
dyld: loaded: /Users/felix/tmp/t2old/Temperature Converter.app/Contents/MacOS/objc-class-proxies.so
dyld: loaded: /Users/felix/tmp/t2old/Temperature Converter.app/Contents/MacOS/objc-class-proxies-bin.so
dyld: loaded: /Users/felix/tmp/t2old/Temperature Converter.app/Contents/MacOS/cocoa.so
...
convertToFar: called; sender #<objc-instance <NSButton: 0x21327d0>>